Fragment Caching
================

Fragment caching refers to caching a fragment of a page. For example, if a
page displays a summary of yearly sale in a table, we can store this table
in cache to eliminate the time needed to generate it for each request.

To use fragment caching, we call
[CController::beginCache()|CBaseController::beginCache()] and
[CController::endCache()|CBaseController::endCache()] in a controller's
view script. The two methods mark the beginning and the end of the page
content that should be cached, respectively. Like [data
caching](/doc/guide/caching.data), we need an ID to identify the fragment
being cached.

~~~
[php]
...other HTML content...
<?php if($this->beginCache($id)) { ?>
...content to be cached...
<?php $this->endCache(); } ?>
...other HTML content...
~~~

In the above, if [beginCache()|CBaseController::beginCache()] returns
false, the cached content will be automatically inserted at the place;
otherwise, the content inside the `if`-statement will be executed and be
cached when [endCache()|CBaseController::endCache()] is invoked.

Caching Options
---------------

When calling [beginCache()|CBaseController::beginCache()], we can supply
an array as the second parameter consisting of caching options to customize
the fragment caching. As a matter of fact, the
[beginCache()|CBaseController::beginCache()] and
[endCache()|CBaseController::endCache()] methods are a convenient wrapper
of the [COutputCache] widget. Therefore, the caching options can be initial
values for any properties of [COutputCache].

### Duration

Perhaps the most commonly option is [duration|COutputCache::duration]
which specifies how long the content can remain valid in cache. It is
similar to the expiration parameter of [CCache::set()]. The following code
caches the content fragment for at most one hour:

~~~
[php]
...other HTML content...
<?php if($this->beginCache($id, array('duration'=>3600))) { ?>
...content to be cached...
<?php $this->endCache(); } ?>
...other HTML content...
~~~

If we do not set the duration, it would default to 60, meaning the cached
content will be invalidated after 60 seconds.

Starting from version 1.1.8, if the duration is set 0, any existing cached
content will be removed from the cache. If the duration is a negative value,
the cache will be disabled, but existing cached content will remain in the cache.
Prior to version 1.1.8, if the duration is 0 or negative, the cache will be disabled.

### Dependency

Like [data caching](/doc/guide/caching.data), content fragment being
cached can also have dependencies. For example, the content of a post being
displayed depends on whether or not the post is modified.

To specify a dependency, we set the [dependency|COutputCache::dependency]
option, which can be either an object implementing [ICacheDependency] or a
configuration array that can be used to generate the dependency object. The
following code specifies the fragment content depends on the change of
`lastModified` column value:

~~~
[php]
...other HTML content...
<?php if($this->beginCache($id, array('dependency'=>array(
		'class'=>'system.caching.dependencies.CDbCacheDependency',
		'sql'=>'SELECT MAX(lastModified) FROM Post')))) { ?>
...content to be cached...
<?php $this->endCache(); } ?>
...other HTML content...
~~~

### Variation

Content being cached may be variated according to some parameters. For
example, the personal profile may look differently to different users. To
cache the profile content, we would like the cached copy to be variated
according to user IDs. This essentially means that we should use different
IDs when calling [beginCache()|CBaseController::beginCache()].

Instead of asking developers to variate the IDs according to some scheme,
[COutputCache] is built-in with such a feature. Below is a summary.

   - [varyByRoute|COutputCache::varyByRoute]: by setting this option
to true, the cached content will be variated according to
[route](/doc/guide/basics.controller#route). Therefore, each combination
of the requested controller and action will have a separate cached content.

   - [varyBySession|COutputCache::varyBySession]: by setting this option
to true, we can make the cached content to be variated according to session
IDs. Therefore, each user session may see different content and they are
all served from cache.

   - [varyByParam|COutputCache::varyByParam]: by setting this option to an
array of names, we can make the cached content to be variated according to
the values of the specified GET parameters. For example, if a page displays
the content of a post according to the `id` GET parameter, we can specify
[varyByParam|COutputCache::varyByParam] to be `array('id')` so that we can
cache the content for each post. Without such variation, we would only be
able to cache a single post.

   - [varyByExpression|COutputCache::varyByExpression]: by setting this option
to a PHP expression, we can make the cached content to be variated according
to the result of this PHP expression.

### Request Types

Sometimes we want the fragment caching to be enabled only for certain
types of request. For example, for a page displaying a form, we only want
to cache the form when it is initially requested (via GET request). Any
subsequent display (via POST request) of the form should not be cached
because the form may contain user input. To do so, we can specify the
[requestTypes|COutputCache::requestTypes] option:

~~~
[php]
...other HTML content...
<?php if($this->beginCache($id, array('requestTypes'=>array('GET')))) { ?>
...content to be cached...
<?php $this->endCache(); } ?>
...other HTML content...
~~~

Nested Caching
--------------

Fragment caching can be nested. That is, a cached fragment is enclosed
within a bigger fragment that is also cached. For example, the comments are
cached in an inner fragment cache, and they are cached together with the
post content in an outer fragment cache.

~~~
[php]
...other HTML content...
<?php if($this->beginCache($id1)) { ?>
...outer content to be cached...
	<?php if($this->beginCache($id2)) { ?>
	...inner content to be cached...
	<?php $this->endCache(); } ?>
...outer content to be cached...
<?php $this->endCache(); } ?>
...other HTML content...
~~~

Different caching options can be set to the nested caches. For example,
the inner cache and the outer cache in the above example can be set with
different duration values. Even when the data cached in the outer cache is
invalidated, the inner cache may still provide the valid inner fragment.
However, it is not true vice versa. If the outer cache is evaluated to be valid,
it will continue to provide the same cached copy even after the content in the
inner cache has been invalidated.
You must be careful in setting the durations or the dependencies of the nested caches,
otherwise the outdated inner fragments may be kept in the outer fragment.

